#include "NightCSV.h"


namespace NE { namespace CsvParser
{

	///////////////////////////////////// Lookup Tables /////////////////////////////////////

	// New line (\n \r)
	const unsigned char lookup_new_line[256] = 
	{
	//  0   1   2   3   4   5   6   7   8   9   A   B   C   D   E   F
		0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  1,  0,  0,  1,  0,  0,  // 0
		0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  // 1
		0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  // 2
		0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  // 3
		0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  // 4
		0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  // 5
		0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  // 6
		0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  // 7
		0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  // 8
		0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  // 9
		0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  // A
		0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  // B
		0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  // C
		0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  // D
		0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  // E
		0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0   // F
	};

	// Text (anything but , \n \r)
	const unsigned char lookup_text[256] = 
	{
	//  0   1   2   3   4   5   6   7   8   9   A   B   C   D   E   F
		1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  0,  1,  1,  0,  1,  1,  // 0
		1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // 1
		1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  0,  1,  1,  1,  // 2
		1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // 3
		1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // 4
		1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // 5
		1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // 6
		1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // 7
		1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // 8
		1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // 9
		1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // A
		1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // B
		1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // C
		1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // D
		1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // E
		1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1   // F
	};


	///////////////////////////////////// CsvItem /////////////////////////////////////


	CsvItem::CsvItem(void)
		: mString(NULL)
		, mSize(0)
	{
		//
	}


	CsvItem::CsvItem(const CsvItem& item)
		: mString(item.mString)
		, mSize(item.mSize)
	{
		//
	}


	CsvItem::CsvItem(const char* str, unsigned int size)
		: mString(str)
		, mSize(size)
	{
		//
	}


	///////////////////////////////////// CsvDocument /////////////////////////////////////


	CsvDocument::CsvDocument()
		: mColumnCount(0)
		, mRowCount(0)
	{
		mItemTable.reserve(2048);	// TODO: init

		mFreshItem = mStaticItemPool;
		mEndItem = mStaticItemPool + STATIC_POOL_SIZE;
	}


	CsvDocument::~CsvDocument()
	{
		std::list<CsvItem*>::iterator iItem, iItemEnd;
		for (iItem = mDynamicPools.begin(), iItemEnd = mDynamicPools.end(); iItem != iItemEnd; ++iItem)
			delete[] *iItem;
		mDynamicPools.clear();
	}


	void CsvDocument::parse(const char* text)
	{
		assert(text);

		mColumnCount = 0;
		mRowCount = 0;

		const char* pText = NULL;
		unsigned int nLength = 0;
		std::vector<CsvItem*> vRow;
		unsigned int nColumnCount = 0;

		while (true)
		{
			if (*text == 0)
				break;

			if (*text == ',')
			{
				++nColumnCount;
				mColumnCount = (nColumnCount > mColumnCount) ? nColumnCount : mColumnCount;
				++text;		// TODO: '\0'
			}
			else if (lookup_new_line[*text])
			{
				++text;
				if (nColumnCount == 0)
					continue;
				++mRowCount;
				nColumnCount = 0;
				mItemTable.push_back(vRow);
				vRow.clear();
			}
			else
			{
				pText = text++;
				nLength = 1;
				while (lookup_text[*text])
					++text, ++nLength;
				vRow.push_back(allocateItem(pText, nLength));
			}
		}
	}


	CsvItem* CsvDocument::allocateItem(const char* text, unsigned int size)
	{
		if (mFreshItem == mEndItem)
		{
			mFreshItem = new CsvItem[DYNAMIC_POOL_SIZE];	// TODO: pooling?
			mEndItem = mFreshItem + DYNAMIC_POOL_SIZE;
			mDynamicPools.push_back(mFreshItem);
		}

		mFreshItem->mString = text;
		mFreshItem->mSize = size;
		return mFreshItem++;
	}

}}